﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace HimVoiceStudioCore
{
    public class ComposingService
    {
        public static string ApiBase = "http://127.0.0.1:12759/";

        public static string CachePath = "audiocache";

        public static List<string> speakerList = new List<string>();

        private static WebClient _client;

        private static WebClient Client
        {
            get
            {
                _client = _client ?? new WebClient();
                return _client;
            }
        }

        public static List<string> GetSpeakerList()
        {
            string str = Client.DownloadString(ApiBase.TrimEnd('/')+"/get_speakers");
            speakerList.Clear();
            speakerList.AddRange(str.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries));
            return speakerList;
        }

        public static List<string> GetCachedSpeakerList()
        {
            if(speakerList.Count == 0)
            {
                GetSpeakerList();
            }
            return speakerList;
        }


        public static bool IsServerUp(int timeout = 1000)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ApiBase.TrimEnd('/') + "/get_speakers");
                request.Timeout = timeout;
                request.ReadWriteTimeout = timeout;
                request.ContinueTimeout = timeout;
                using(HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
                {
                    using(StreamReader sr = new StreamReader(resp.GetResponseStream()))
                    {
                        sr.ReadToEnd();
                        return true;
                    }
                }
            }catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }
        }
        [DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        public static bool TryStartServer(string baseDir)
        {
            var pythonfile = Path.Combine(baseDir, "voicesvc.exe");
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.UseShellExecute = true;
            psi.WorkingDirectory = Path.GetFullPath(baseDir);
            psi.FileName = pythonfile;
            var ps = Process.Start(psi);
            while(!ps.HasExited)
            {
                if (IsServerUp(500))
                {
                    if (ps.MainWindowHandle != IntPtr.Zero) { 
                        ShowWindow(ps.MainWindowHandle, 2);
                    }
                    return true;
                }
            }
            return false;
        }

        public static byte[] Compose(string str,ComposingParam param)
        {
            return File.ReadAllBytes(ComposeAndGetPath(str,param));
        }

        private static PreloadAssets assetProvider;
        
        public static void SetAssetProvider(PreloadAssets _assetProvider)
        {
            assetProvider = _assetProvider;
        }

        public static string ComposeAndGetPath(string str, ComposingParam param)
        {
            GetCachedSpeakerList();
            param = param.Clone();
           

            if (param.emotion == null || param.emotion == "default_emo.npz")
            {
                param.emotion = "default_emo.npz";
            }
            else
            {
                var emokey = param.emotion;
                var node = assetProvider.GetEmotionReference(emokey);
                if (node != null)
                {
                    param.emotion = node.FilePath;
                }
                else
                {
                    throw new ArgumentException("未知的情感参考ID：" + emokey);
                }
            }

            RequestParam requestParam = new RequestParam()
            {
                text = str,
                param = param
            };

         
            string cacheFile = Path.Combine(CachePath, requestParam.GetHash() + ".wav");
            if (!File.Exists(cacheFile))
            {
                var data = Client.DownloadData(ApiBase.TrimEnd('/') + "/infer?" + requestParam.ToUrlParam());
                File.WriteAllBytes(cacheFile + ".dl", data);
                File.Move(cacheFile + ".dl", cacheFile);
            }
            return cacheFile;
        }


    }

    public class ComposingParam
    {
       
        public string emotion = "default_emo.npz";
        public int seed = 42;
        public SpliceParam spliceParam { get; internal set; }

        public ComposingParam Clone()
        {
            return new ComposingParam
            {
                spliceParam = spliceParam == null ? null : new SpliceParam() { start = spliceParam.start, end = spliceParam.end },
                emotion = emotion,
                seed = seed
            };
        }

        internal ComposingParam withSeedAndEmo(ComposingParam currentParam)
        {
            this.emotion = currentParam.emotion;
            this.seed = currentParam.seed;
            return this;
        }
    }

    public class RequestParam
    {
        public string text;
        public ComposingParam param;

        public string ToUrlParam()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("text="+HttpUtility.UrlEncode(text));
            sb.Append("&seed=" + param.seed);
            sb.Append("&emo="+HttpUtility.UrlEncode(param.emotion));
            return sb.ToString();
        }

        private static SHA256 _sha1Instance = null;
        private static SHA256 SHA1
        {
            get
            {
                return (_sha1Instance = _sha1Instance ?? SHA256.Create());
            }
        }

        public string GetHash()
        {
            return BitConverter.ToString(SHA1.ComputeHash(Encoding.UTF8.GetBytes(ToUrlParam()))).ToLower().Replace("-","");
        }
    }
}
